
#import "AsyncHttpRequest.h"

@implementation AsyncHttpRequest

//
// protocol
//
@synthesize protocol														= _protocol;

//
// domain
//
@synthesize domain															= _domain;

//
// port
//
@synthesize port															= _port;

//
// path
//
@synthesize relPath															= _relPath;

//
// auth user name
//
@synthesize authUserName													= _authUserName;

//
// auth password
//
@synthesize authPassword													= _authPassword;

//
// request
//
@synthesize request															= _request;

//
// post data
//
@synthesize postData														= _postData;

//
// connection
//
@synthesize	connection														= _connection;

//
// response
//
@synthesize response														= _response;

//
// error
//
@synthesize error															= _error;

//
// data buffer
//
@synthesize dataBuffer														= _dataBuffer;

//
// callback
//
@synthesize callback														= _callback;

//
// auto redirect
//
@synthesize autoRedirect													= _autoRedirect;

//
// dealloc
//
- (void)dealloc
{
	self.protocol															= nil;
	self.domain																= nil;
	self.relPath															= nil;
	self.authUserName														= nil;
	self.authPassword														= nil;
	self.request															= nil;
	self.postData															= nil;
	self.connection															= nil;
	self.response															= nil;
	self.error																= nil;
	self.dataBuffer															= nil;
	self.callback															= nil;
	
	[super dealloc];
}

//
// setup request
//
- (void)setupRequest
{
	//
	// setup url
	//
	self.autoRedirect														= FALSE;
	NSString* url															= nil;
	if(self.port)
		url																	= [NSString stringWithFormat:@"%@://%@:%d/%@", self.protocol, self.domain, self.port, self.relPath];
	else
		url																	= [NSString stringWithFormat:@"%@://%@/%@", self.protocol, self.domain, self.relPath];
	
	self.request															= [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];
	[self.request setHTTPShouldHandleCookies:NO];
}

//
// start
//
- (void)startWithCallback:(AsyncHttpRequestFinishCallback)callback
{
	[self setupRequest];
	
	if(self.postData.length)
	{
		[self.request setHTTPMethod:@"POST"];
		[self.request setValue:[NSString stringWithFormat:@"%d", self.postData.length] forHTTPHeaderField:@"Content-Length"];
		[self.request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
		[self.request setHTTPBody:self.postData];
	}
	else
	{
		[self.request setHTTPMethod:@"GET"];
	}
	
	self.response															= nil;
	self.error																= nil;
	self.connection															= [NSURLConnection connectionWithRequest:self.request delegate:self];
	self.callback															= callback;
	[self.connection start];
}

//
// redirect
//
- (NSURLRequest*)connection:(NSURLConnection*)connection willSendRequest:(NSURLRequest*)request redirectResponse:(NSURLResponse*)response
{
	return (self.autoRedirect || !response) ? request : nil;
}

//
// failed
//
- (void)connection:(NSURLConnection*)connection didFailWithError:(NSError*)error
{
	self.error																= error;
	self.callback(self);
}

//
// received response
//
- (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLResponse*)response
{
	self.response															= (NSHTTPURLResponse*)response;
	self.dataBuffer															= [NSMutableData dataWithCapacity:1024];
}

//
// received data
//
- (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data
{
	[self.dataBuffer appendData:data];
}

//
// finished
//
- (void)connectionDidFinishLoading:(NSURLConnection*)connection
{
	NSInteger statusCode													= self.response.statusCode;
	if(!self.error && statusCode >= 300)
		[self setErrorMessage:[NSString stringWithFormat:@"HTTP Request failed with %d", statusCode] code:-statusCode domain:@"HttpErrorDomain"];
	self.callback(self);
}

//
// auth
//
- (BOOL)connection:(NSURLConnection*)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace*)protectionSpace
{
	return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust] || [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodHTTPBasic];
}

//
// auth challenge
//
- (void)connection:(NSURLConnection*)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge*)challenge
{
	if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
	{
		[challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
		[challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
	}
	else if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodHTTPBasic])
	{
		[challenge.sender useCredential:[NSURLCredential credentialWithUser:self.authUserName password:self.authPassword persistence:NSURLCredentialPersistenceNone] forAuthenticationChallenge:challenge];
	}
}

//
// error description
//
- (NSString*)errorDescription
{
	return self.error ? self.error.localizedDescription : NSLocalizedString(@"Internal Error", nil);
}

//
// setup cookie
//
- (NSHTTPCookie*)cookieWithName:(NSString*)cookieName value:(NSString*)value expires:(NSDate*)expires
{
	NSMutableDictionary* cookieProperties									= [NSMutableDictionary dictionaryWithCapacity:5];
	[cookieProperties setObject:self.domain forKey:NSHTTPCookieDomain];
	[cookieProperties setObject:self.relPath forKey:NSHTTPCookiePath];
	[cookieProperties setObject:cookieName forKey:NSHTTPCookieName];
	[cookieProperties setObject:value forKey:NSHTTPCookieValue];
	[cookieProperties setObject:expires forKey:NSHTTPCookieExpires];
	return [NSHTTPCookie cookieWithProperties:cookieProperties];
}

//
// set error message
//
- (void)setErrorMessage:(NSString*)message code:(NSInteger)code domain:(NSString*)domain
{
	NSDictionary* userInfo													= [NSDictionary dictionaryWithObject:message forKey:NSLocalizedFailureReasonErrorKey];
	self.error																= [NSError errorWithDomain:domain code:code userInfo:userInfo];
}
@end
